<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>VUE实现无限滚动demo4</title>
    <style>
        .box {
            width: 100%;
            text-align: center;
            vertical-align: middle;
            border: 1px solid #777;
            height: 200px;
            font-size: 24px;
        }
    </style>
</head>
<body>
<div id="app">
    <div v-for="d in view" class="box">
        {{d}}
    </div>
</div>
<script src="https://cdn.staticfile.org/vue/2.5.22/vue.js"></script>
<script>
    /**
     * demo4
     * 在demo4的基础上改进
     */
    let vm = new Vue({
        el: '#app',
        data: {
            // 单页个数
            size: 10,
            // 当前页码
            page: 0,
            // 历史最大翻到过页码
            maxPage: 1,
            // 历史最大滚动条与顶部距离
            lastScrollTop: 0,
            // 后台总共的页码
            totalPage: 10,
            // 展示用array
            view: [],
            // 模拟后台数据array
            back: []
        },
        methods: {
            /**
             * @param direction 0 上 1 下
             */
            initView: function (direction) {
                switch (direction) {
                    case 0: {
                        console.log("===> up" + this.page);
                        this.view.unshift(...this.back.slice(
                            (this.page - 3) * this.size,
                            (this.page - 2) * this.size
                        ));
                        document.querySelector('#app').style.paddingTop = (200 * (this.page - 3) * this.size) + 'px';
                        this.view.splice(this.view.length - this.size, this.view.length);
                        this.page--;
                        // 向上滚判断底部是否需要增加padding
                        if(this.maxPage > this.page){
                            document.querySelector('#app').style.paddingBottom = (200 * (this.maxPage - this.page) * this.size) + 'px';
                        }
                        break;
                    }
                    case 1: {
                        console.log("===> down " + this.page);
                        this.view.push(...this.back.slice(
                            this.page * this.size,
                            (this.page + 1) * this.size
                        ));
                        if (this.page > 1) {
                            this.view.splice(0, this.size);
                            document.querySelector('#app').style.paddingTop = (200 * (this.page - 1) * this.size) + 'px';
                        }
                        this.page++;
                        // 向下滚判断是否需要减少底部padding
                        if(this.maxPage > this.page){
                            console.log("===> maxPage =  " + this.maxPage + ' page =' + this.page);
                            document.querySelector('#app').style.paddingBottom = (200 * (this.maxPage - this.page) * this.size) + 'px';
                        }
                        if (this.maxPage < this.page) {
                            this.maxPage = this.page;
                        }
                        break;
                    }

                }

            }
        },
        created: function () {
            window.onscroll = function () {
                // 滚动条距离顶部
                const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
                // 屏幕显示区域高度
                const windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
                // 滚动条总长
                const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
                if (vm.lastScrollTop - scrollTop > 0) {
                    // 向上滚动
                    // 判断白色padding即将进入屏幕才加载更多
                    // 首先是从page = 2以后才减少底部dom，所以差是2
                    // 如果page=6，即 缺少4 * 10 * 200px的空白区
                    // 若滚动条距顶部小于等于空白区 就是需要触发initview的时间 (预留windowHeight来反应)
                    // console.log(((vm.page - 2) * vm.size * 200 + windowHeight) + '>=' + scrollTop)
                    if (vm.page > 2 && ((vm.page - 2) * vm.size * 200 + windowHeight) >= scrollTop) {
                        vm.initView(0);
                    }
                } else {
                    // 滚动条不含屏幕距离顶部 + 屏幕区域 >= 滚动条总长 可以认为触底
                    // 但个别浏览器的计算方式有可能ZZ，建议预留部分区域
                    // 这里的代码把整个 屏幕区域 作为预留
                    // 也就是 滚动条不含屏幕距离顶部 + 屏幕区域 + 屏幕区域 >= 滚动条总长
                    // 另外页码不允许大于等于后台总页码
                    // demo 4 新增条件，若 maxPage > page , 且 padding 区域即将进入屏幕，触发
                    console.log('======> ' + ((vm.maxPage - vm.page) * vm.size * 200 + windowHeight) + '>=' + scrollTop)
                    if ( (vm.maxPage > vm.page) &&  ((vm.maxPage - vm.page) * vm.size * 200 + windowHeight) >= scrollTop
                        || vm.page < vm.totalPage && scrollTop + (windowHeight * 2) >= scrollHeight) {
                        vm.initView(1);
                    }
                }

                vm.lastScrollTop = scrollTop;
            };
        },
        mounted: function () {
            for (let i = 0; i < this.totalPage * this.size; i++) {
                this.back.push(i);
            }
            this.initView(1);
        }
    });
</script>
</body>
</html>